#include "Recordable.h"
#include "dwmapi.h"
#include "psapi.h"

std::string get_window_name(HWND _h) {
    wchar_t name[100];
    GetWindowTextW(_h, name, 100);
    return wide_char_to_multi_byte(name);
}

void release_recordable(Recordable* _recordable)
{
    ReleaseDC(_recordable->hwnd, _recordable->hdc);
    delete _recordable;
}

bool check_window_valid(HWND _window) {
    RECT rect;
    DWORD styles, ex_styles, cloaked;
    if (!IsWindowVisible(_window) || IsIconic(_window)) {
        return false;
    }
    HRESULT hr = DwmGetWindowAttribute(_window, DWMWA_CLOAKED, &cloaked, sizeof(cloaked));
    if (hr >= 0 && cloaked) {
        return false;
    }
    GetClientRect(_window, &rect);
    styles = GetWindowLongPtrW(_window, GWL_STYLE);
    ex_styles = GetWindowLongPtrW(_window, GWL_EXSTYLE);

    if (styles & WS_CHILD) {
        return false;
    }

    if (ex_styles & WS_EX_TOOLWINDOW) {
        return false;
    }

    if (rect.bottom == 0 || rect.right == 0) {
        return false;
    }

    return true;
}

HWND next_window(HWND _window, HWND _parent) {
    while (true) {
        _window = FindWindowExW(_parent, _window, NULL, NULL);
        if (!_window || check_window_valid(_window)) {
            break;
        }
    }
    return _window;
}


std::string wide_char_to_multi_byte(wchar_t* pWCStrKey)
{
    //第一次调用确认转换后单字节字符串的长度，用于开辟空间
    int pSize = WideCharToMultiByte(CP_OEMCP, 0, pWCStrKey, wcslen(pWCStrKey), NULL, 0, NULL, NULL);
    char* pCStrKey = new char[pSize + 1];
    //第二次调用将双字节字符串转换成单字节字符串
    WideCharToMultiByte(CP_OEMCP, 0, pWCStrKey, wcslen(pWCStrKey), pCStrKey, pSize, NULL, NULL);
    pCStrKey[pSize] = '\0';
    std::string result = std::string(pCStrKey);
    delete[] pCStrKey;
    return result;
}

::Recordable get_recordable(HWND _hwnd)
{
    Recordable result = { 0 };
    result.hwnd = _hwnd;
    wchar_t name[100];
    GetWindowTextW(_hwnd, name, 100);
    result.name = wide_char_to_multi_byte(name);
    RECT rect = { 0 };
    GetWindowRect(_hwnd, &rect);
    result.width = rect.right - rect.left;
    result.height = rect.bottom - rect.top;
    result.hdc = GetWindowDC(_hwnd);
    return result;
}

void find_all_recordable_window(std::function<void(Recordable*)> _callback)
{
    RECT rect;
    HWND parent = GetDesktopWindow();
    HWND window = next_window(NULL, parent);
    Recordable* recordable = nullptr;
    while (window) {
        GetClientRect(window, &rect);
        recordable = new Recordable;
        recordable->hwnd = window;
        recordable->hdc = GetDC(window);
        recordable->width = rect.right;
        recordable->height = rect.bottom;
        recordable->name = get_window_name(window);
        get_window_exe(&recordable->exe_name, window);
        get_window_class(&recordable->class_name, window);
        _callback(recordable);
        window = next_window(window, parent);
    }
}

void get_window_exe(std::string* _window_exe, HWND _window)
{
    DWORD process_id;
    HANDLE process = nullptr;
    GetWindowThreadProcessId(_window, &process_id);
    if (process_id == GetCurrentProcessId()) {
        return;
    }
    process = OpenProcess(PROCESS_QUERY_LIMITED_INFORMATION, false, process_id);
    if (process == nullptr) {
        return;
    }
    
    wchar_t exe_name_temp[MAX_PATH];
    if (!GetProcessImageFileNameW(process, exe_name_temp, MAX_PATH)) {
        CloseHandle(process);
        return;
    }
    std::string tmp = wide_char_to_multi_byte(exe_name_temp);
    auto index = tmp.find_last_of("\\");
    if (index == -1) {
        CloseHandle(process);
        return;
    }
    index += 1;
    *_window_exe = tmp.substr(index);

    CloseHandle(process);
    return;
}

void get_window_class(std::string* _window_class, HWND _window)
{
    wchar_t temp[256];
    temp[0] = 0;
    GetClassNameW(_window, temp, sizeof(temp) / sizeof(wchar_t));
    *_window_class = wide_char_to_multi_byte(temp);
}
